#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#include <map>
#include <list>

#include "nodelevel_aggregator.h"
#include "event_manager.h"
#include "dfruntime.h"
#include "misc.h"


#define MAX_EVT_CODE 256

// TODO :
// Make sure events with non thread local reach have thread safe queues.






typedef std::multimap<PtrType,action_p> evt_map;
typedef std::list<action_p> evt_list;

typedef std::pair<evt_map::iterator, evt_map::iterator> evt_maprange;
typedef std::list<delegate_lock_p> lock_list;

struct evt_desc {
    evthdl hdl;
    void * arg;
    bool any;
};



struct action {
    Closure * c;
    lock_list outer_locks;
    int counter;

    void perform() {

            if ( outer_locks.empty() ) {
                (*c)();
            } else {
                delegate_lock_p d = outer_locks.front();
                outer_locks.pop_front();

                delegate_lock_do( d, dfrt_get_thread_num(),closure({ perform(); }) );
            }



    }



};



struct evt_handle {

    evt_handle() {
        map = NULL;
        list = NULL;

        if ( properties.evtp_is_thread_local ) {
            inner_lock = new_delegate_lock();
        }

        map = new evt_map();
        list = new evt_list();

    }

    void register_action( evt_desc *e, action *a ) {
            if ( properties.evtp_is_thread_local ) {
                do_register_action(e,a);
            } else {
                delegate_lock_do(inner_lock, dfrt_get_thread_num(), closure({do_register_action(e,a);}));
            }
    }



    void do_register_action( evt_desc *ed,  action_p a ) {
        if ( properties.evtp_has_argument && ! ed->any) {
            // Normal case for one argument.
            map->insert( std::pair<PtrType,action_p>( ed->arg, a ) );
            a->counter ++;
        } else if ( !properties.evtp_has_argument || ed->any ){
            // For argument-less events.
            list->push_front(a);
            a->counter++;
        }

        if ( properties.evtp_is_state_event ) {
            if ( properties.state_indicator(ed->arg) ) {
                do_signal_event(ed);
            }
        }

    }


    inline void signal_event( evt_desc *e ) {
            if ( properties.evtp_is_thread_local ) {
                do_signal_event(e);
            } else {

                delegate_lock_do(inner_lock,dfrt_get_thread_num(), closure({ do_signal_event( e ) ;}));
            }
    }


    void do_signal_event( evt_desc *ed ) {
        if ( properties.evtp_has_argument ) {
            if ( ed->arg != EVT_ANY ) {
                evt_maprange range = map->equal_range( ed->arg );
                evt_map::iterator i = range.first;
                while ( i != range.second ) {
                    ASSERT( ed->arg == i->first );

                    i->second->counter --;
                    ASSERT(i->second->counter >= 0 );
                    if ( i->second->counter == 0 ) {
                        i->second->perform();
                        delete i->second;
                    }
                    ++i;
                }

                map->erase(ed->arg);

            } else {

                // Special case handling EVT_ANY
                evt_map::iterator it = map->begin();
                while ( it != map->end() ) {
                    it->second->counter --;
                    ASSERT(it->second->counter >= 0 );
                    if ( it->second->counter == 0 ) {
                        it->second->perform();
                        delete it->second;
                    }
                }
                map->clear();
              }


        }

        evt_list::iterator it = list->begin();

        for (; it != list->end(); ++it ) {
            (*it)->counter --;

            if ( (*it)->counter == 0 ) {
                (*it)->perform();
                delete *it;
            }
        }
        list->clear();
    }




    evt_properties properties;

    delegate_lock_p inner_lock;
    evt_map * map;
    evt_list * list;

};




evthdl init_event( struct evt_properties * properties ) {

    evthdl ret = new evt_handle();
    memcpy( &ret->properties,properties, sizeof( evt_properties) );

    // Thread local events are synchronous, and state events have a way to deal with asynchronicity, through test after register event.
    // One must register events of one type or another.
    ASSERT(ret->properties.evtp_is_thread_local || ret->properties.evtp_is_state_event );
    ASSERT(ret->properties.evtp_is_thread_local || ret->properties.lock != NULL);
    return ret;
}







void on_event( Closure * c, ...) {

    va_list arglist;
    va_start( arglist, c );

    struct action * act = new action;
    act->c = c;
    act->counter = 0;

    while ( true )  {
        evthdl e = va_arg( arglist, evthdl);

        if( e == NULL) {
            break;
        }


        PtrType arg = va_arg( arglist, PtrType );



        struct evt_desc desc;
        desc.hdl = e;
        desc.arg = arg;



        e->register_action(&desc, act);

    }


    va_end( arglist );

    // Check if event should be performed instantly :
    act->counter --;
    if ( act->counter == 0  ) {
        act->perform();
    }

}

void signal_event( evthdl e, void * arg ) {
    evt_desc desc;
    desc.any = (arg == EVT_ANY);
    desc.arg = arg;
    desc.hdl = e;

    e->signal_event(&desc);
}



